package com.cby.user.test3D;

import lombok.Data;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/*
**  Create by: 王孙康宏
    Date:2021-10-24
    Time:22:09
*/
@Data
public class SinglePacking {
    UpdateUtil updateUtil;
    Solution bestSolution;
    Solution tempSolution;
    Solution currSolution;
    // 最大迭代次数
    int epochs = 500;
    // 当前迭代次数
    int t = 0;
    // 实例
    Instance instance;
    // 最佳实例
    Instance bestInstance;
    // 已经放置的物品
    List<PlaceBox> placeBoxList;
    // 计时，到规定时间就停止迭代
    long time = 0l;
    long startTime;
    /**
     * 构造函数
     */
    public SinglePacking(Instance instance,long time){
        updateUtil = new UpdateUtil();
        bestSolution = new Solution();
        tempSolution = new Solution();
        currSolution = new Solution();
        this.instance = instance;
        placeBoxList = new ArrayList<>();
        this.time = time;
    }

    /**
     * 主要求解函数
     */
    public Solution solve(){
        startTime = System.currentTimeMillis();
        while (t<epochs){
//            System.out.println(t);
            // 1.对实例进行评估 获取对应的可行解
            currSolution = evaluate(instance);
            // 2.若当前可行解装载率大于最优解 则进行替换
            if(currSolution.getPackRate()>bestSolution.getPackRate()){
                bestSolution = currSolution;
                bestInstance = instance;
                System.out.println("第"+t+"次迭代，空间利用率为："+bestSolution.getPackRate());
            }
            // 3.对当前实例进行次序更新
            instance = updateBoxes(instance);
            t++;
        }
        // 4.迭代结束 返回最佳可行解
        return bestSolution;
    }
    /**
     * 评估函数
     */
    public Solution evaluate(Instance instance){
        Solution solution = new Solution();
        Box[] boxes = copyBoxes(instance.getBoxes());

        solution.setBoxes(copyBoxes(boxes));
        // **按照规则 进行装箱**
        State state = initState(); // 初始装载状态
        for (int i = 0; i < boxes.length; i++) {
            if(System.currentTimeMillis()-startTime>=time){
                break;
            }
            state.setFeasible(true);
            for (int j = 0; j < boxes[i].getCount(); j++) {
                if(System.currentTimeMillis()-startTime>=time){
                    break;
                }
                // 按照顺序选取box
                Box box = boxes[i];
                // 放入容器 更新状态
                state = updateState(state,box);
                // 如果当前状态不可行 则说明箱子没装完 容器就满了 跳出内部循环 遍历下一种箱子
                if(!state.isFeasible){
                    boxes[i].setCount(0);
                    j = boxes[i].getCount();
                    i = 0;
                }
            }
        }
        placeBoxList = new ArrayList<>(state.getPlacedBoxList());
        // 将已经放置好的箱子集合存入解集 PlaceBox[]::new
        PlaceBox[] placeBoxes = new PlaceBox[placeBoxList.size()];
        for (int i = 0; i < placeBoxList.size(); i++) {
            placeBoxes[i] = placeBoxList.get(i);
        }
        solution.setPlaceBoxes(copyPlaceBoxes(placeBoxes));
        // 计算装载率
        float totalBoxS = 0f;
        for (PlaceBox placeBox : placeBoxList) {
            totalBoxS += placeBox.getL()*placeBox.getW()*placeBox.getH();
        }
        solution.setPackRate((totalBoxS/instance.getS()));
        solution.setInstance(instance);
        return solution;
    }
    /**
     * box次序变异函数
     */
    public Instance updateBoxes(Instance instance){
        return updateUtil.updateBoxes(instance);
    }
    /**
     * 初始化装载状态
     */
    public State initState(){
        State state = new State();
        List<Space> spaceList = new ArrayList<>();
        spaceList.add(new Space(0,0,0,instance.getL(),instance.getW(),instance.getH()));
        state.setResidueSpace(new ArrayList<>(spaceList));
        state.setPlacedBoxList(new ArrayList<PlaceBox>());
        return state;
    }
    /**
     * 更新装载状态
     */
    public State updateState(State state,Box box){
        // 1.选取box方向尺寸列表中的第一个元素作为方向
        float[] orientation = box.getOrientations()[0];
        // 2.获取当前剩余空间
        List<Space> residueSpaceList = new ArrayList<>(state.getResidueSpace());
        // 2.根据当前剩余空间寻找放置位置(使用第一个可行的剩余空间)
        PlaceBox placeBox = null;
        int spaceIndex = -1;
        for (int i = 0; i < residueSpaceList.size(); i++) {
            Space space = residueSpaceList.get(i);
            if(theSpaceIsFeasible(space,box,state.getPlacedBoxList())){
                spaceIndex = i;
                placeBox = new PlaceBox(space.x1,space.y1,space.z1,box.getL(),box.getW(),box.getH(),box.l,box.w,box.h);
                break;
            }
        }
        if(spaceIndex==-1){
            state.isFeasible = false;
            return state;
        }
        // 3.将placeBox添加到state的placedboxList中
        state.getPlacedBoxList().add(placeBox);
        // 4.更新已放置的物品列表
        state.setPlacedBoxList(state.getPlacedBoxList());
        // 5.更新剩余空间
        state.setResidueSpace(new ArrayList<>(updateSpace(residueSpaceList,placeBox,spaceIndex)));
        return state;
    }
    /**
     * 更新剩余空间 并按照字典序排序
     * 例 如 ， 有 剩 余 空 间 S = {𝑆1, 𝑆2, 𝑆3} ， 其 中 𝑆1= {(2,3,5), (10,10,10)}
     * ， 𝑆2={(4,6,4), (10,10,10)}，𝑆3= {(2,6,7), (10,10,10)}，
     * 则按字典序递增排序后为𝑆1, 𝑆3, 𝑆2，
     */
    public List<Space> updateSpace(List<Space> spaceList,PlaceBox placeBox,int spaceIndex){
        Space useSpace = spaceList.remove(spaceIndex);
        // 使用一个剩余空间会新增3个剩余空间
        // 第一个 x，y不变 z变
        Space space1 = new Space(useSpace);
        space1.setZ1(useSpace.getZ1()+placeBox.getH());
        // 第一个 x，z不变 y变
        Space space2 = new Space(useSpace);
        space2.setY1(useSpace.getY1()+placeBox.getW());
        // 第一个 y，z不变 x变
        Space space3 = new Space(useSpace);
        space3.setX1(useSpace.getX1()+placeBox.getL());
        // 将新增的剩余空间加入剩余空间列表
        spaceList.add(space1);
        spaceList.add(space2);
        spaceList.add(space3);
        // 按字典序进行升序排序
        Collections.sort(spaceList);
        return spaceList;
    }
    /**
     * 判断当前box放在当前剩余空间是否可行
     */
    public boolean theSpaceIsFeasible(Space space,Box box,List<PlaceBox> placeBoxList){

        if(space.x1+box.getL()>instance.getL()){
            return false;
        }else if(space.y1+box.getW()>instance.getW()){
            return false;
        }else if(space.z1+box.getH()>instance.getH()){
            return false;
        }

        for (PlaceBox placeBox : placeBoxList) {
            boolean overlap1 = isOverlap(placeBox.x, placeBox.l, space.x1, box.getL());
            boolean overlap2 = isOverlap(placeBox.y, placeBox.w, space.y1, box.getW());
            boolean overlap3 = isOverlap(placeBox.z, placeBox.h, space.z1, box.getH());
            if(overlap1&&overlap2&&overlap3){
                //如果三个方向都重叠 说明box之间有重叠
                return false;
            }
        }

        return true;
    }
    /**
     * 判断两个箱子某方向是否重叠
     */
    public boolean isOverlap(float p1,float p2,float w1,float w2){
        if(p1>w1){
            return w1+w2>p1;
        }else if(p1<w1){
            return p1+p2>w1;
        }else{
            // p1==w1
            return true;
        }
    }
    /**
     * 深拷贝Box对象数组
     */
    public Box[] copyBoxes(Box[] boxes){
        Box[] ret = new Box[boxes.length];
        for (int i = 0; i < boxes.length; i++) {
            ret[i] = new Box(boxes[i]);
        }
        return ret;
    }
    /**
     * 深拷贝Box对象数组
     */
    public PlaceBox[] copyPlaceBoxes(PlaceBox[] placeBoxes){
        PlaceBox[] ret = new PlaceBox[placeBoxes.length];
        for (int i = 0; i < placeBoxes.length; i++) {
            ret[i] = new PlaceBox(placeBoxes[i]);
        }
        return ret;
    }
}
